using System;
using System.Xml;
using System.Xml.Schema;
using System.Reflection;
using System.Configuration;

namespace XDL.Framework.Configuration
{
    /// <summary>
    /// Provides support for XML configuration section.
    /// </summary>
    /// <remarks>
    /// The class provides the framework for schema validation.
    /// </remarks>
    /// <example>
    /// <code>
    ///		&lt;configuration&gt;
    ///			&lt;configSections&gt;
    ///				&lt;section name="test1" type="Toolkit.XmlConfigurationSection,Toolkit.dll" /&gt;
    ///			&lt;/configSections&gt;
    ///			...
    /// 		&lt;test1&gt;
    /// 			&lt;hello&gt;world&lt;/hello&gt;
    /// 		&lt;/test1&gt;
    ///		&lt;/configuration&gt;
    ///	</code>
    /// The following code retrieve the content of a simple 'test1' configuration section:
    /// <code>
    /// XmlNode xn = (XmlNode)ConfigurationSettings.GetConfig("test1");
    /// </code>
    /// The <see cref="XmlNode"/> contains the following Xml:
    /// <code>
    /// 	&lt;?xml version="1.0" encoding="utf-8" ?&gt;
    /// 	&lt;test1&gt;
    /// 		&lt;hello&gt;world&lt;/hello&gt;
    /// 	&lt;/test1&gt;
    /// </code>
    /// </example>
    public class XmlConfigurationSection :
        IConfigurationSectionHandler
    {
        /// <summary>
        /// Initializes a configuration reader.
        /// </summary>
        public XmlConfigurationSection()
        {
        }

        /// <summary>
        /// Implemented by all configuration section handlers to parse the XML of the configuration section. The returned object is added to the configuration collection and is accessed by GetConfig.
        /// </summary>
        /// <param name="parent">The configuration settings in a corresponding parent configuration section.</param>
        /// <param name="configContext">An <c>System.Web.HttpConfigurationContext</c> when Create is called from the ASP.NET configuration system. Otherwise, this parameter is reserved and is a null reference (Nothing in Visual Basic). </param>
        /// <param name="section">The <see cref="XmlNode"/> that contains the configuration information from the configuration file. Provides direct access to the XML contents of the configuration section.</param>
        /// <returns>The configuration section as a <see cref="XmlNode"/> object.</returns>
        public virtual object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            Validate(section);
            return CreateWrapper(section);
        }

        /// <summary>
        /// Validates the configuration section.
        /// </summary>
        /// <param name="section">The <see cref="XmlNode"/> that contains the configuration information from the configuration file. Provides direct access to the XML contents of the configuration section.</param>
        public virtual void Validate(XmlNode section)
        {
            m_settings.ValidationType = ValidationType.Schema;
            m_settings.ValidationFlags = XmlSchemaValidationFlags.None;
            m_settings.XmlResolver = null;
            XmlReader vr = XmlReader.Create(new XmlNodeReader(section), m_settings);
            
            try
            {
                while (vr.Read()) ;
            }
            catch (Exception e)
            {
                throw new ConfigurationException("Unable to validate custom configuration section.", e, section);
            }
        }

        /// <summary>
        /// Event handler for the validation.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The <see cref="ValidationEventArgs"/> parameters.</param>
        /// <exception cref="XmlSchemaException">The configuration section does not follow the schema definition.</exception>
        protected virtual void ValidationHandler(object sender, ValidationEventArgs args)
        {
            switch (args.Severity)
            {
                case XmlSeverityType.Error:
                    throw new XmlSchemaException(args.Message, new Exception("Schema validation failure"));
                case XmlSeverityType.Warning:
                    // does nothing
                    break;
            }
        }

        /// <summary>
        /// Append the child nodes of <c>importSection</c> under <c>rootNode</c>.
        /// </summary>
        /// <param name="xd">The XML document that contains <c>rootNode</c>.</param>
        /// <param name="rootNode">The root node.</param>
        /// <param name="importSection">The parent of the nodes to append.</param>
        protected static void AppendChildNodes(XmlDocument xd, XmlNode rootNode, XmlNode importSection)
        {
            if (rootNode != null)
            {
                foreach (XmlNode xn in importSection.ChildNodes)
                {
                    rootNode.AppendChild(xd.ImportNode(xn, true));
                }
            }
        }

        /// <summary>
        /// Override this function to return a custom object (instead of the default XmlNode)
        /// </summary>
        /// <remarks>
        /// Default behavior returns the incoming section parameter.
        /// </remarks>
        /// <param name="section">Incoming XML section</param>
        /// <returns>An object that the client will work with</returns>
        protected virtual object CreateWrapper(System.Xml.XmlNode section)
        {
            return section;
        }

        // default reader settings
        XmlReaderSettings m_settings = new XmlReaderSettings();

        /// <summary>
        /// Get/Set validation schemas
        /// </summary>
        public XmlSchemaSet Schemas
        {
            get
            {
                return m_settings.Schemas;
            }
        }
    }
}
